package com.lge.clock.worldclock.map;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.opengl.GLUtils;

import com.lge.clock.worldclock.util.Log;

/**
 * @author lge.com
 */
public class Shape {
    private static final String TAG = "Shape";
    
	private int mBitmapArrNum = 0;
	public static Bitmap mBitmaps[] = new Bitmap[3];
	
	public static final int FLOAT_TYPE_SIZE = 4;
	public static final int SHORT_TYPE_SIZE = 2;
	
	public static final int SHAPE_ROTATE_COORDINATE_NONE = 0;
	public static final int SHAPE_ROTATE_COORDINATE_X = 1;
	public static final int SHAPE_ROTATE_COORDINATE_Y = 2;
	public static final int SHAPE_ROTATE_COORDINATE_Z = 3;
	
	protected FloatBuffer mVertexBuffer = null;
	protected ShortBuffer mIndexBuffer = null;
	protected FloatBuffer mColorBuffer = null;
	protected FloatBuffer mNormalBuffer = null;
	protected FloatBuffer mTextureBuffer = null;
	
	protected int mVertexSize = -1;
	protected int mIndicesSize = -1;
	
	protected float mRGBAValue[] = {1.0f, 1.0f, 1.0f, 1.0f};
	
	protected float mTranslateX = 0.0f;
	protected float mTranslateY = 0.0f;
	protected float mTranslateZ = 0.0f;
	
	protected float mRotateX = 0.0f;
	protected float mRotateY = 0.0f;
	protected float mRotateZ = 0.0f;
	
	protected float mScaleSize = 1.0f;
	
	private boolean mIsTranslateProcess = false;
	private boolean mIsRotateProcess = false;
	private boolean mIsScaleProcess = false;
	
	protected boolean mIsColorsProcess = false;
	protected boolean mIsTextureProcess = false;
	
	protected boolean mIsUsingRotateLimit = false;
	protected int mRotateLimitCoordinate = SHAPE_ROTATE_COORDINATE_NONE;
	protected float mRotateMinimumSize = -1.0f;
	protected float mRotateMaximumSize = -1.0f;
	
	protected boolean mIsUsingScaleLimit = false;
	protected float mScaleMinimumSize = -1.0f;
	protected float mScaleMaximumSize = -1.0f;
	
	protected float[] mInitRotateValue;
	protected int mInitRotateCoordinate = SHAPE_ROTATE_COORDINATE_NONE;
	
	protected int mRenderMode = GL10.GL_TRIANGLES;
	
	protected int[] mBindTextures = new int[1];
	
	public void setInitRotate(float x, float y, float z) {
		if (mInitRotateValue == null) {
			mInitRotateValue = new float[3];
		}
		//suein1209.kim INTERNAL_TEST.WBT_11 - Defect #297448
		if (Float.compare(x, 0) != 0) {
		    mInitRotateValue[0] = x;
		}
		//suein1209.kim INTERNAL_TEST.WBT_11 - Defect #297449 
		if (Float.compare(y, 0) != 0) {
		    mInitRotateValue[1] = y;
		}
		//suein1209.kim INTERNAL_TEST.WBT_11 - Defect #297450
		if (Float.compare(z, 0) != 0) {
		    mInitRotateValue[2] = z;
		}
	}
	
	public void doInitRotate(GL10 gl) {
		if (mInitRotateValue == null) return;
		
		//suein1209.kim INTERNAL_TEST.WBT_11 - Defect #297451
		if (Float.compare(mInitRotateValue[0], 0) != 0) {
		    gl.glRotatef(mInitRotateValue[0], 1, 0, 0);
        }
		//suein1209.kim INTERNAL_TEST.WBT_11 - Defect #297452
        if (Float.compare(mInitRotateValue[1], 0) != 0) {
            gl.glRotatef(mInitRotateValue[1], 0, 1, 0);
        }
        //suein1209.kim INTERNAL_TEST.WBT_11 - Defect #297453
        if (Float.compare(mInitRotateValue[2], 0) != 0) {
            gl.glRotatef(mInitRotateValue[2], 0, 0, 1);
        }
	}
	
	public boolean isTranslateProcess() {
		return mIsTranslateProcess;
	}
	
	public void setTranslateProcess(boolean isTranslate) {
		mIsTranslateProcess = isTranslate;
	}
	
	public boolean isRotateProcess() {
		return mIsRotateProcess;
	}

	public void setRotateProcess(boolean isRotate) {
		mIsRotateProcess = isRotate;
	}
	
	public boolean isScaleProcess() {
		return mIsScaleProcess;
	}

	public void setScaleProcess(boolean isScale) {
		mIsScaleProcess = isScale;
	}
	
	public void setRenderMode(int mode) {
		mRenderMode = mode;
	}
	
	public void setVertexBuffer(float[] vertices) {
		ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * FLOAT_TYPE_SIZE);
		vbb.order(ByteOrder.nativeOrder());
		mVertexBuffer = vbb.asFloatBuffer();
		mVertexBuffer.put(vertices);
		mVertexBuffer.position(0);
		mVertexSize = vertices.length / 3;
	}
	
	public int getVertexCount() {
		return mVertexSize;
	}
	
	public void setIndexBuffer(short[] indices) {
		ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * SHORT_TYPE_SIZE);
		ibb.order(ByteOrder.nativeOrder());
		mIndexBuffer = ibb.asShortBuffer();
		mIndexBuffer.put(indices);
		mIndexBuffer.position(0);
		mIndicesSize = indices.length;
	}
	
	public void setColorsProcess(boolean isProcess) {
		mIsColorsProcess = isProcess;
	}
	
	public void setColorBuffer(float[] colors) {
		ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * FLOAT_TYPE_SIZE);
		cbb.order(ByteOrder.nativeOrder());
		mColorBuffer = cbb.asFloatBuffer();
		mColorBuffer.put(colors);
		mColorBuffer.position(0);
	}
	
	public void setColor(int color) {
	    float red = 0.0f;
	    float green = 0.0f;
	    float blue = 0.0f;
	    float alpha = 0.0f;
	    
	    alpha   = 1.0f/255.0f * ((color >> 24) & 0xFF);
	    red     = 1.0f/255.0f * ((color >> 16) & 0xFF);
	    green   = 1.0f/255.0f * ((color >> 8) & 0xFF);
	    blue    = 1.0f/255.0f * (color & 0xFF);
	    
	    setColor(red, green, blue, alpha);
	}
	
	public void setNormalBuffer(float[] normals) {
		ByteBuffer nbb = ByteBuffer.allocateDirect(normals.length * FLOAT_TYPE_SIZE);
		nbb.order(ByteOrder.nativeOrder());
		mNormalBuffer = nbb.asFloatBuffer();
		mNormalBuffer.put(normals);
		mNormalBuffer.position(0);
	}
	
	public void setTextureProcess(boolean isProcess) {
		mIsTextureProcess = isProcess;
	}
	
	public boolean isTextureProcess() {
		return mTextureBuffer != null && mIsTextureProcess;
	}
	
	public void setTextureBuffer(float[] textures) {
		ByteBuffer tbb = ByteBuffer.allocateDirect(textures.length * FLOAT_TYPE_SIZE);
		tbb.order(ByteOrder.nativeOrder());
		mTextureBuffer = tbb.asFloatBuffer();
		mTextureBuffer.put(textures);
		mTextureBuffer.position(0);
	}
	
	public void setColor(float red, float green, float blue, float alpha) {
		mRGBAValue[0] = red;
		mRGBAValue[1] = green;
		mRGBAValue[2] = blue;
		mRGBAValue[3] = alpha;
    }
	
	public void setTranslate(float x, float y, float z) {
		mTranslateX = x;
		mTranslateY = y;
		mTranslateZ = z;
	}
	
	public void setAppendTranslate(float x, float y, float z) {
		mTranslateX += x;
		mTranslateY += y;
		mTranslateZ += z;
	}
	
	public void setRoateLimit(int coordinate, float min, float max) {
		mIsUsingRotateLimit = true;
		
		mRotateLimitCoordinate = coordinate;
		mRotateMinimumSize = min;
		mRotateMaximumSize = max;
	}
	
	public void setRotate(float rx, float ry, float rz) {
		mRotateX = rx;
		mRotateY = ry;
		mRotateZ = rz;
		
		checkRotateLimit();
	}
	
	public void setAppendRotate(float rx, float ry, float rz) {
		mRotateX += rx;
		mRotateY += ry;
		mRotateZ += rz;
		
		checkRotateLimit();
	}
	
	public void checkRotateLimit() {
		if (mIsUsingRotateLimit) {
			switch (mRotateLimitCoordinate) {
				case SHAPE_ROTATE_COORDINATE_X: {
					if (mRotateX < mRotateMinimumSize) {
						mRotateX = mRotateMinimumSize;
					} else if (mRotateX > mRotateMaximumSize) {
						mRotateX = mRotateMaximumSize;
					}
					break;
				}
				case SHAPE_ROTATE_COORDINATE_Y: {
					if (mRotateY < mRotateMinimumSize) {
						mRotateY = mRotateMinimumSize;
					} else if (mRotateY > mRotateMaximumSize) {
						mRotateY = mRotateMaximumSize;
					}
					break;
				}
				case SHAPE_ROTATE_COORDINATE_Z: {
					if (mRotateZ < mRotateMinimumSize) {
						mRotateZ = mRotateMinimumSize;
					} else if (mRotateZ > mRotateMaximumSize) {
						mRotateZ = mRotateMaximumSize;
					}
					break;
				}
				default :
			}
		}
	}
	
	public void setScaleLimit(float min, float max) {
		mIsUsingScaleLimit = true;
		
		mScaleMinimumSize = min;
		mScaleMaximumSize = max;
		
		checkScaleLimit();
	}
	
	public void setScale(float size) {
		mScaleSize = size;
		
		checkScaleLimit();
	}
	
	public void setAppendScale(float size) {
		mScaleSize += size;
		
		checkScaleLimit();
	}
	
	public void checkScaleLimit() {
		if (!mIsUsingScaleLimit) return;
		
		if (mScaleSize < mScaleMinimumSize) {
			mScaleSize = mScaleMinimumSize;
		} else if (mScaleSize > mScaleMaximumSize) {
			mScaleSize = mScaleMaximumSize;
		}
	}
	
	public float getScale() {
		return mScaleSize;
	}
	
	public void doTranslatef(GL10 gl) {
		gl.glTranslatef(mTranslateX, mTranslateY, mTranslateZ);
	}
	
	public void doRotatef(GL10 gl) {
		gl.glRotatef(mRotateX, 1, 0, 0);
		gl.glRotatef(mRotateY, 0, 1, 0);
		gl.glRotatef(mRotateZ, 0, 0, 1);
	}
	
	public void doScalef(GL10 gl) {
		gl.glScalef(mScaleSize, mScaleSize, mScaleSize);
	}
	
	public void draw(GL10 gl) {
		if (isTranslateProcess() || isRotateProcess() || isScaleProcess()) {
			gl.glLoadIdentity();
			
            if (isTranslateProcess()) doTranslatef(gl);
            
            if (isRotateProcess()) doRotatef(gl);
            
            doInitRotate(gl);
            
            if (isScaleProcess()) doScalef(gl);
		}
		
		// Counter-clockwise winding.
		gl.glFrontFace(GL10.GL_CCW);
		// Enable face culling.
		gl.glEnable(GL10.GL_CULL_FACE);
		// What faces to remove with the face culling.
		gl.glCullFace(GL10.GL_BACK);
		
		// Enabled the vertices buffer for writing and to be used during
		// rendering.
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		// Specifies the location and data format of an array of vertex
		// coordinates to use when rendering.
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
		
		if (mNormalBuffer != null) {
            gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
            gl.glNormalPointer(GL10.GL_FLOAT, 0, mNormalBuffer);
        }

		// Set flat color
		if (mTextureBuffer != null && mIsTextureProcess) {
            // color set, texture set - multiplication.
            gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
            
            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
            gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer);
        } else {
            // Set flat color
            gl.glColor4f(mRGBAValue[0], mRGBAValue[1], mRGBAValue[2], mRGBAValue[3]);
            // Smooth color
            if (mColorBuffer != null && mIsColorsProcess) {
                // Enable the color array buffer to be used during rendering.
                gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
                // Point out the where the color buffer is.
                gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
            }
        }

		if (mIndexBuffer != null) {
            gl.glDrawElements(mRenderMode, mIndicesSize,
                    GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
        } else {
            gl.glDrawArrays(mRenderMode, 0, mVertexSize);
        }
		
		// Disable the vertices buffer.
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        
        if (mNormalBuffer != null) {
            gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
        }
        
        if (mTextureBuffer != null && mIsTextureProcess) {
            gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        } else {
            if (mColorBuffer != null && mIsColorsProcess) {
                // Disable the colors buffer.
                gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
            }
        }
        
		// Disable face culling.
		gl.glDisable(GL10.GL_CULL_FACE);
	}
	
	public void loadGLTexture(GL10 gl, Context context, int imgName) {
	    Log.message(TAG, "loadGLTexture() - S");		
	    
		if (mBitmaps[mBitmapArrNum] == null){
		    Log.message(TAG, "loadGLTexture() - image decode - S");
//			mBitmaps[mBitmapArrNum] = BitmapFactory.decodeResource(context.getResources(), imgName);
			Options options = new BitmapFactory.Options();
			options.inJustDecodeBounds = true;
			BitmapFactory.decodeResource(context.getResources(), imgName, options);
			int scaleW = options.outWidth / 720 + 1;
			int scaleH = options.outHeight / 1280 + 1;
			int scale = Math.max(scaleW, scaleH);
			android.util.Log.e("1234567489", "scaleW => " + scaleW + " , scaleH => " + scaleH);
			options.inJustDecodeBounds = false;
			options.inSampleSize = scale;
			mBitmaps[mBitmapArrNum] = BitmapFactory.decodeResource(context.getResources(), imgName, options);			
			Log.message(TAG, "loadGLTexture() - image decode - E");
		}
        
        //Generate one texture pointer...
        gl.glGenTextures(1, mBindTextures, 0);
        //...and bind it to our array
        gl.glBindTexture(GL10.GL_TEXTURE_2D, mBindTextures[0]);

        //Create Nearest Filtered Texture
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        //Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);

        //Use the Android GLUtils to specify a two-dimensional texture image from our bitmap
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmaps[mBitmapArrNum], 0);
        
        //Clean up
        Log.message(TAG, "loadGLTexture() - E");
    }
	
	public void loadGLTexture(GL10 gl, Bitmap image) {
        Log.message(TAG, "loadGLTexture() param change - S");
        
        //Generate one texture pointer...
        gl.glGenTextures(1, mBindTextures, 0);
        //...and bind it to our array
        gl.glBindTexture(GL10.GL_TEXTURE_2D, mBindTextures[0]);

        //Create Nearest Filtered Texture
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        //Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);

        //Use the Android GLUtils to specify a two-dimensional texture image from our bitmap
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, image, 0);
        
        //Clean up
        image.recycle();
        image = null;
        Log.message(TAG, "loadGLTexture() param change - E");
    }

	public void setmBitmapArrNum(int mBitmapArrNum) {
		this.mBitmapArrNum = mBitmapArrNum;
	}

	public int getmBitmapArrNum() {
		return mBitmapArrNum;
	}
}
